package nl.fw.yapool.examples;

import java.util.concurrent.atomic.AtomicLong;

import nl.fw.yapool.IPoolFactory;
import nl.fw.yapool.PoolPruner;
import nl.fw.yapool.PrunedPool;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Demonstrates validation of resources when they are released/checked in.
 * Some database pools have this as an option. Yapool's PrunedPool/SQLPool 
 * only validates before resources are acquired (and it that validation fails,
 * tries to acquire another one or create one).
 * Validation before checkin/release can be implemented using a listener, 
 * but this demonstration simply extends PrunedPool's release method.
 * The comments in the source code below explains the log-statements generated by the main-method: <pre>{@literal
27:45:677 [main] INFO yapool.example.validate - Starting ExampleValidateOnCheckIn
27:45:695 [main] INFO yapool.example.validate - Created 1
27:45:695 [main] INFO yapool.example.validate - Created 2
27:45:696 [main] INFO yapool.example.validate - Validated 2
27:45:696 [main] INFO yapool.example.validate - Validated 1
27:45:697 [main] INFO yapool.example.validate - Invalidated 1 upon release.
27:45:697 [main] INFO yapool.example.validate - Evicted 1
27:45:697 [main] INFO yapool.example.validate - Destroyed 1
27:45:697 [main] INFO yapool.example.validate - Validated 2 upon release.
27:45:697 [main] INFO yapool.example.validate - Pool size: 1
27:45:697 [main] INFO yapool.example.validate - Validated 2
27:45:697 [main] INFO yapool.example.validate - Validated 2 upon release.
27:45:697 [main] INFO yapool.example.validate - Closing pool
27:45:698 [main] INFO yapool.example.validate - Destroyed 2
27:45:700 [main] INFO yapool.example.validate - Finished ExampleValidateOnCheckIn
}</pre>
 * @author Fred
 *
 */
public class ExampleValidateOnCheckIn {

	public static final String LOG_CATEGORY = "yapool.example.validate";
	
	private static final Logger log = LoggerFactory.getLogger(LOG_CATEGORY);
	
	public static void main(String[] args) {

		log.info("Starting " + ExampleValidateOnCheckIn.class.getSimpleName());
		ExampleValidateOnCheckIn ev = new ExampleValidateOnCheckIn();
		try {
			ev.demonstrate();
		} catch (Exception e) {
			log.error("ValidateOnCheckIn example failed to run.", e);
		} finally {
			// Make sure the scheduled executor service used by PoolPruner is stopped.
			PoolPruner.getInstance().stop();
		}
		log.info("Finished " + ExampleValidateOnCheckIn.class.getSimpleName());
	}

	public void demonstrate() throws Exception {
		
		PrunedPool<Long> pool = new PrunedPool<Long>() {
			
			private final Logger log = LoggerFactory.getLogger(LOG_CATEGORY);

			@Override
			public Long release(Long l) {
				
				/*
				 * Normally the validation would be done by simply calling
				 * isValid(l);
				 * which calls the factory isValid-method within a try-catch block.
				 * But for this example, odd numbers are only invalid upon release.
				 */
				if (l % 2 == 0) {
					log.info("Validated " + l + " upon release.");
				} else {
					log.info("Invalidated " + l + " upon release.");
					// Since the evicted resource is also released (see below), 
					// the resource does not have to be destroyed now.
					if (evictLeased(l, false)) {
						log.info("Evicted " + l);
					} else {
						log.error("Something is very wrong, the resource was not in the leased resources list of the pool.");
					}
				}
				/*
				 * The pool will destroy any released resources that are no longer part of the pool. 
				 * Since odd numbers are evicted (see above), only even numbers will be released back into the pool.
				 * All odd numbers will be destroyed.
				 */
				return super.release(l);
			}
		};
		// 
		pool.setFactory(new LongFactory());

		// When opening the pool, the "isValid" method from the factory is not called for created resources.
		// Newly created resources are always considered valid.
		pool.open(2);
		
		// Last created resource is acquired first.
		// Resource is validated by factory before returned by acquire().
		Long l2 = pool.acquire();
		Long l1 = pool.acquire();
		// Resource 1 will be evicted and destroyed upon release.
		pool.release(l1);
		// Resource 2 is valid and will return to the pool.
		pool.release(l2);
		log.info("Pool size: " + pool.getSize());
		// This will acquire and release resource 2.
		pool.release(pool.acquire());
		log.info("Closing pool");
		pool.close();
	}
	
	class LongFactory implements IPoolFactory<Long> {

		private final Logger log = LoggerFactory.getLogger(LOG_CATEGORY);

		private AtomicLong f = new AtomicLong();
		
		@Override
		public Long create() {
			Long l = f.incrementAndGet();
			log.info("Created " + l);
			return l;
		}

		@Override
		public boolean isValid(Long l) {
			log.info("Validated " + l);
			return true;
		}

		@Override
		public void destroy(Long l) {
			log.info("Destroyed " + l);
		}
		
	}
	
}
